package cn.openea.perpartner.cms.web.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.openea.perpartner.cms.factory.UserContext;
import cn.openea.perpartner.cms.utils.CosUpload;
import cn.openea.perpartner.cms.web.dto.QrCodeDto;
import cn.openea.perpartner.cms.web.dto.SyncDto;
import cn.openea.perpartner.cms.web.entity.*;
import cn.openea.perpartner.cms.web.mapper.SysUserRoleMapper;
import cn.openea.perpartner.cms.web.service.*;
import cn.openea.perpartner.common.core.config.SystemConfig;
import cn.openea.perpartner.common.core.constant.Constants;
import cn.openea.perpartner.system.util.ToolsUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * @author Weird077
 * @date 2022-09-13 17:44
 */
@Service
public class CmsQrCodeServiceImpl implements ICmsQrCodeService {
    @Value("${tencent.applets.appid}")
    private String appid;
    @Value("${tencent.applets.secret}")
    private String secret;
    @Value("${tencent.applets.page}")
    private String page;
    @Value("${tencent.applets.env_version}")
    private String env_version;
    @Value("${openea.url}")
    private String openeaUrl;
    @Autowired
    private CosUpload cosUpload;
    @Autowired
    private ICmsCustomerService customService;
    @Autowired
    private ICmsCommunityService communityService;
    @Autowired
    private ICmsSalesService salesService;
    @Autowired
    private ICmsSalesAssistantService assistantService;
    @Autowired
    private ICmsUserService userService;

    @Autowired
    private SysUserRoleMapper userRoleMapper;

    @Override
    public JSONObject createCustomerCode(QrCodeDto codeDto) {
        CmsCustomer customer = customService.getById(codeDto.getId());
        return createPicture(codeDto, Constants.CRM_CUSTOMER, BeanUtil.beanToMap(customer));
    }

    @Override
    public JSONObject createSalesAssistantCode(QrCodeDto codeDto) {
        CmsSalesAssistant salesAssistant = assistantService.getById(codeDto.getId());
        return createPicture(codeDto, Constants.CRM_ASSISTANT, BeanUtil.beanToMap(salesAssistant));
    }

    @Override
    public JSONObject createSalesCode(QrCodeDto codeDto) {
        CmsSales sales = salesService.getById(codeDto.getId());
        return createPicture(codeDto, Constants.CRM_SALES, BeanUtil.beanToMap(sales));
    }

    @Override
    public JSONObject createCommunityCode(QrCodeDto codeDto) {
        CmsCommunity community = communityService.getById(codeDto.getId());
        return createPicture(codeDto, Constants.CRM_COMMUNITY, BeanUtil.beanToMap(community));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String sync(SyncDto syncDto) {
        if (syncDto.getGroup().equals(Constants.CRM_CUSTOMER)) {
            return syncCustomer(syncDto);
        }
        if (syncDto.getGroup().equals(Constants.CRM_COMMUNITY)) {
            return syncCommunity(syncDto);
        }
        if (syncDto.getGroup().equals(Constants.CRM_SALES)) {
            return syncSales(syncDto);
        }
        if (syncDto.getGroup().equals(Constants.CRM_ASSISTANT)) {
            return syncAssistant(syncDto);
        }
        return "";
    }

    /**
     * 将用户同步至Crm用户
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncUser(SyncDto syncDto) {
        LambdaQueryWrapper<CmsUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CmsUser::getUserId, syncDto.getUserId());
        CmsUser user = userService.getOne(wrapper);
        if (BeanUtil.isNotEmpty(user)) {
            LambdaUpdateWrapper<CmsUser> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(CmsUser::getUserId,syncDto.getUserId());
            updateWrapper.set(CmsUser::getNickName,syncDto.getNickName());
            updateWrapper.eq(CmsUser::getUserId, syncDto.getUserId());
            userService.update(updateWrapper);
        } else {
            user = new CmsUser();
            user.setUserId(syncDto.getUserId());
            user.setNickName(syncDto.getNickName());
            userService.save(user);
        }
    }

    /**
     * 同步品牌商家用户
     */
    @Transactional(rollbackFor = Exception.class)
    public String syncCustomer(SyncDto syncDto) {
        syncUser(syncDto);
        String msg = "请勿重复注册！";
        //1、查询品牌上级基本数据
        LambdaQueryWrapper<CmsCustomer> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CmsCustomer::getOpeneaId, syncDto.getOpeneaId());
        CmsCustomer customer = customService.getOne(wrapper);
        if (BeanUtil.isNotEmpty(customer)) {
            //2.更新品牌商家基本数据
            customer.setOpeneaId(syncDto.getUserId());
            customer.setSyncStatus("1");
            customService.updateById(customer);
            msg = "注册成功！";
        }
        return msg;
    }

    /**
     * 同步社区团长用户
     */
    @Transactional(rollbackFor = Exception.class)
    public String syncCommunity(SyncDto syncDto) {
        syncUser(syncDto);
        String msg = "请勿重复注册！";
        //1、查询社区团长基本数据
        LambdaQueryWrapper<CmsCommunity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CmsCommunity::getOpeneaId, syncDto.getOpeneaId());
        CmsCommunity community = communityService.getOne(wrapper);
        //2、更新社区团长基本数据
        if (BeanUtil.isNotEmpty(community)) {
            community.setOpeneaId(syncDto.getUserId());
            community.setUserName(syncDto.getNickName());
            community.setSyncStatus("1");
            communityService.updateById(community);
            msg = "注册成功！";
        }
        return msg;
    }

    /**
     * 同步销售用户
     */
    @Transactional(rollbackFor = Exception.class)
    public String syncSales(SyncDto syncDto) {
        syncUser(syncDto);
        String msg = "请勿重复注册！";
        //1、更新销售表基本信息
        //1.1、查询销售基本数据
        LambdaQueryWrapper<CmsSales> salesWrapper = new LambdaQueryWrapper<>();
        salesWrapper.eq(CmsSales::getOpeneaId, syncDto.getOpeneaId());
        CmsSales sales = salesService.getOne(salesWrapper);
        //1.2、更新销售基本数据,即更新销售表的用户id，使之与小程序用户id一一致
        if (BeanUtil.isNotEmpty(sales)) {
            sales.setOpeneaId(syncDto.getUserId());
            sales.setUserName(syncDto.getNickName());
            sales.setSyncStatus("1");
            salesService.updateById(sales);
            msg = "注册成功！";
        }
        //2、更新销售角色
        //2.1、查询用户角色
        LambdaQueryWrapper<SysUserRole> userRoleWrapper = new LambdaQueryWrapper<>();
        userRoleWrapper.eq(SysUserRole::getUserId, syncDto.getOpeneaId());
        List<SysUserRole> userRoleList = userRoleMapper.selectList(userRoleWrapper);
        //2.2、更新用户角色
        for (SysUserRole userRole : userRoleList) {
            LambdaUpdateWrapper<SysUserRole> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(SysUserRole::getUserId, syncDto.getOpeneaId());
            updateWrapper.eq(SysUserRole::getRoleId, userRole.getRoleId());
            updateWrapper.set(SysUserRole::getUserId, syncDto.getUserId());
            userRoleMapper.update(userRole, updateWrapper);
            msg = "注册成功！";
        }
        //3、更新销售助理
        //3.1、查询销售助理表
        LambdaQueryWrapper<CmsSalesAssistant> assistantWrapper = new LambdaQueryWrapper<>();
        assistantWrapper.eq(CmsSalesAssistant::getOpeneaId, syncDto.getOpeneaId());
        CmsSalesAssistant assistant = assistantService.getOne(assistantWrapper);
        //3.2、若当前销售也是销售助理，则需要更新销售助理表用户id
        if (BeanUtil.isNotEmpty(assistant)) {
            assistant.setOpeneaId(syncDto.getUserId());
            assistant.setUserName(syncDto.getNickName());
            assistant.setSyncStatus("1");
            assistantService.updateById(assistant);
            msg = "注册成功！";
        }
        return msg;
    }

    /**
     * 同步销售助理用户
     */
    @Transactional(rollbackFor = Exception.class)
    public String syncAssistant(SyncDto syncDto) {
        syncUser(syncDto);
        String msg = "请勿重复注册";
        //1.1、查询助理基本数据
        LambdaQueryWrapper<CmsSalesAssistant> assistantWrapper = new LambdaQueryWrapper<>();
        assistantWrapper.eq(CmsSalesAssistant::getOpeneaId, syncDto.getOpeneaId());
        CmsSalesAssistant assistant = assistantService.getOne(assistantWrapper);
        //1.2、更新助理基本数据
        if (BeanUtil.isNotEmpty(assistant)) {
            assistant.setOpeneaId(syncDto.getUserId());
            assistant.setUserName(syncDto.getNickName());
            assistant.setSyncStatus("1");
            assistantService.updateById(assistant);
            msg = "注册成功！";
        }
        //2.1、查询助理用户角色
        LambdaQueryWrapper<SysUserRole> userRoleWrapper = new LambdaQueryWrapper<>();
        userRoleWrapper.eq(SysUserRole::getUserId, syncDto.getOpeneaId());
        List<SysUserRole> userRoleList = userRoleMapper.selectList(userRoleWrapper);
        //2.2、更新助理用户角色
        for (SysUserRole userRole : userRoleList) {
            LambdaUpdateWrapper<SysUserRole> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(SysUserRole::getUserId, syncDto.getOpeneaId());
            updateWrapper.eq(SysUserRole::getRoleId, userRole.getRoleId());
            updateWrapper.set(SysUserRole::getUserId, syncDto.getUserId());
            userRoleMapper.update(userRole, updateWrapper);
            msg = "注册成功！";
        }
        //3.1、更新销售表，助理id，助理名称
        LambdaQueryWrapper<CmsSales> salesWrapper = new LambdaQueryWrapper<>();
        salesWrapper.eq(CmsSales::getAssistantId, syncDto.getOpeneaId());
        List<CmsSales> saleList = salesService.list(salesWrapper);
        for (CmsSales sales : saleList) {
            sales.setAssistantId(syncDto.getUserId());
            sales.setAssistantName(syncDto.getNickName());
            sales.setSyncStatus("1");
            salesService.updateById(sales);
            msg = "注册成功！";
        }
        return msg;
    }

    /**
     * 生成二维码图片
     *
     * @param codeDto 二维码参数对象
     * @param group   用户分组
     * @param params  用户基本数据
     */
    public JSONObject createPicture(QrCodeDto codeDto, String group, Map<String, Object> params) {
        JSONObject object = new JSONObject();
        object.put("code", "201");
        object.put("msg", "未知错误");

        String url = codeDto.getUrl();
        String unid = codeDto.getId();
        String type = codeDto.getType();
        if (StrUtil.isBlank(url) || StrUtil.isBlank(unid) || StrUtil.isBlank(type)) {
            object.put("msg", "参数信息不能有空值！");
            return object;
        }
        // 1、获取用户分享时生成的一对一对小程序菊花码
        String sceneid = String.valueOf(ToolsUtil.getUniqueID());
        if ((type + "#" + sceneid).length() > 32) {
            int typeLen = 32 - (sceneid.length() + 1);
            object.put("msg", "type参数最多允许（" + typeLen + "）位，当前type参数过长使菊花码参数超过限");
            return object;
        }
        BufferedImage qrcodeImg = generateQrCode(page, type + "#" + sceneid);
        // 2、生成分享时的临时菊花码图片给用户进行处理（用户id信息.png），默认这里分为3组
        JSONObject resobj = createPicture(qrcodeImg, 3);
        if ("201".equals(resobj.getString("code"))) {
            return resobj;
        }
        String webUrl = resobj.getString("weburi");
        object.put("weburi", webUrl);
        // 3、将分享信息写入分享记录表
        params.put("id", codeDto.getId());
        url = url + "?openeaId=" + params.get("openeaId") + "&group=" + group;
        params.put("group",group);
        if (addQRShareInfo(sceneid, url, type, params)) {
            object.put("code", "200");
            object.put("msg", "分享菊花码成功。");
        } else {
            object.put("msg", "写入分享记录时出现错误！");
        }
        return object;
    }

    /**
     * 1、生成小程序二维码
     *
     * @param page  当前小程序相对页面 必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index, 根路径前不要填加 /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
     * @param scene 最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）
     */
    public BufferedImage generateQrCode(String page, String scene) {
        BufferedImage qrcodeImg = null;
        // 发起API请求，返回对应的参数信息
        try {
            // 获取小程序二维码的API地址，带参数
            String wxcodeUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken();
            //调用微信接口生成二维码
            URL url = new URL(wxcodeUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 提交模式
            connection.setRequestMethod("POST");
            // 发送POST请求必须设置如下两行
            connection.setDoOutput(true);
            connection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
            // 小程序二维码的API请求时的参数
            JSONObject paramJson = new JSONObject();
            //这就是你二维码里携带的参数 String型  名称不可变
            paramJson.put("scene", scene);
            paramJson.put("env_version", env_version);
            //注意该接口传入的是page而不是path
            paramJson.put("page", page);
            //这是设置扫描二维码后跳转的页面
            paramJson.put("width", 200);
            paramJson.put("is_hyaline", true);
            paramJson.put("auto_color", true);
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
            qrcodeImg = ImageIO.read(bis);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return qrcodeImg;
    }

    /**
     * 获取小程序access_token
     **/
    private String getAccessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
        String result = HttpUtil.get(url);
        if (StrUtil.isNotBlank(result)) {
            JSONObject wxObject = JSON.parseObject(result);
            return wxObject.getString("access_token");
        }
        return "";
    }

    /**
     * 2、生成分享时的临时菊花码图片给用户进行处理（用户id信息.png），默认这里分为3组
     *
     * @param qrcodeImg  菊花码图像流
     * @param groupCount 分为值，这里默认是3组
     * @return 生成信息
     * code=200成功；code=201失败
     * msg，信息内容，如果是失败，会包含失败的错误信息
     * weburi，如果code=200,则包含这个值，表示入前端浏览器直接访问菊花码的地址
     */
    public JSONObject createPicture(BufferedImage qrcodeImg, int groupCount) {
        JSONObject object = new JSONObject();
        object.put("code", "201");
        object.put("msg", "未知错误");
        if (qrcodeImg == null) {
            object.put("msg", "菊花码为空！");
            return object;
        }
        try {
            // 计算图片存储的临时地址
            String userid = UserContext.getUserId();
            // 计算分组id，这里主要使用用户id的hashCode求余，作为分组id，将用户存到不同的文件夹内
            int groupid = Math.abs(userid.hashCode() % groupCount);
            // 以年月开头记录内容
            String dateStr = DateUtil.format(new Date(), "yyMM") + String.format("%02d", groupid);
            // 在服务器中存储的真实地址
            String serviceuri = SystemConfig.getProfile() + "/sharecode/" + dateStr + "/";
            File file = new File(serviceuri);
            if (!file.exists()) {
                file.mkdirs();
            }
            serviceuri += userid + ".png";

            file = new File(serviceuri);
            // 读入菊花码图片
            ImageIO.write(qrcodeImg, "PNG", file);
            // 在网页中显示的地址,上传cos
            String cosUrl = cosUpload.uploadFile(file, false);
            object.put("code", "200");
            object.put("weburi", cosUrl);
            object.put("msg", "成功生成菊花码");
            //上传cos后，删除本地文件
            if (StrUtil.isNotBlank(cosUrl) && file.exists()){
                file.delete();
            }
        } catch (Exception e) {
            object.put("msg", "菊花码生成文件错误，错误信息为：" + e.getMessage());
            e.printStackTrace();
        }
        return object;
    }

    /**
     * 3、将分享信息写入分享记录表
     *
     * @param sceneid 分享的主键，也是二维码中的一个参数
     * @param url     分享内容进入的地址，必须
     * @param type    分享类别，必须
     * @return 信息是否写入成功，true表示成功，false表示失败
     */
    private boolean addQRShareInfo(String sceneid, String url, String type, Map<String, Object> paramMap) {
        paramMap.put("WF_OrUnid", sceneid);
        paramMap.put("docUnid", paramMap.get("id"));
        paramMap.put("inviter", UserContext.getUserId());
        paramMap.put("inviterName", UserContext.getUserName());
        paramMap.put("type", type);
        // 设置过期时间为当前时间1年后
        paramMap.put("expireTime", getExpireTime(1));
        // 这里写的是扫码注册后最终的目标地址
        paramMap.put("shareAddress", url);
        paramMap.put("shareSource", paramMap.get("id"));
        paramMap.put("openeaId", paramMap.get("openeaId"));
        paramMap.put("group", paramMap.get("group"));
        HashMap<String, Object> param = new HashMap<>();
        param.put("crmMap", BeanUtil.toBean(paramMap, JSONObject.class));
        String result = HttpRequest.post(openeaUrl)
                .header("sysid", "SysAdmin")
                .header("syspwd", "pass")
                .header("userId", "admin")
                .form(param)
                .execute().body();
        JSONObject obj = JSON.parseObject(result);
        return "200".equals(obj.getString("code"));
    }

    /**
     * 将当前时间向前加 year 年的 TimeInMillis 数
     *
     * @param year 需要增加的年数，至少1年起步
     * @return 增加年数后反回的 TimeInMillis
     */
    private String getExpireTime(int year) {
        if (year == 0) {
            year = 1;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.YEAR, year);
        return String.valueOf(calendar.getTimeInMillis());
    }

}
